home *** CD-ROM | disk | FTP | other *** search
/ Aminet 21 / Aminet 21 (1997)(GTI - Schatztruhe)[!][Oct 1997].iso / Aminet / dev / misc / Gfx4PCQ.lha / WindowLib / Examples / Lander / Lander.p < prev    next >
Encoding:
Text File  |  1997-07-19  |  21.3 KB  |  629 lines

  1. Program Lander;
  2. { ************************************************************************
  3.   **    Lander                                **
  4.   ** A sample game using the PCQ windowlib © 1997 THOR-Software inc.    **
  5.   ** Version 0.00 11.07.1997                        **
  6.   **                                    **
  7.   ** This game is by no means complete, it's just a demonstration    **
  8.   ** what's possible with the windowlib and should serve as a reference **
  9.   ** for common techniques for accessing the library.            **
  10.   ** Feel free to complete this example if you like!            **
  11.   ************************************************************************}
  12.  
  13. {$I "INCLUDE:Utils/windowlib.i"}    { This is the main include file.
  14.                       All windowlib entries are defined
  15.                       here }
  16. {$I "INCLUDE:Utils/random.i"}        { A random generator }
  17.  
  18.  
  19. { Next, we define the types for the definition of the sprite shapes. They
  20.   have to be arrays of PCQ type strings, the size of the array beeing the
  21.   height of the sprite. }
  22. TYPE
  23.     LanderArray    =    Array [0..9] of String;    { The shape of the lander itself }
  24.     FlameArray    =    Array [0..4] of String; { The main engine }
  25.     SteerArray    =    Array [0..2] of String; { The left and right engine }
  26.  
  27. { This is the definition of the sprite shapes. Since we're using true hardware
  28.   sprites in this example, the width of the sprites are limited to 16 pixels
  29.   and a maximum of three colors is allowed. The shape itself is encoded in
  30.   "ASCII-Art", each character representing one color:
  31.     space    =    background
  32.     .    =    color 1
  33.     +    =    color 2
  34.     *    =    color 3
  35.   If you're using bobs,i.e. playfield graphics instead of hardware sprites, more
  36.   colors than the four above can be used. The encoding can be found in
  37.   the docs. }
  38.  
  39. CONST
  40.     Lander    :    LanderArray=(    { the lander itself }
  41.     "      ...       ",
  42.     "     ......     ",
  43.     "    ........    ",
  44.     "   ...+..+...   ",
  45.     "  *..++..++..*  ",
  46.     "   ...+..+...   ",
  47.     "    ........    ",
  48.     "  ..  ***   ..  ",
  49.     " ..  *****   .. ",
  50.     "..            ..");
  51.      
  52.     { the main engine. This one takes two shapes since we're going
  53.       to animate it. This animation is automatically done by the
  54.       windowlib, no need to care about it. }
  55.  
  56.     Flame    :    Array[0..1] of FlameArray=((
  57.     "      .*.       ",
  58.     "     .*+*.      ",
  59.     "     .*+*.      ",
  60.     "      ...       ",
  61.     "                "),(
  62.     "      .*.       ",
  63.     "     .*+*.      ",
  64.     "     **+**      ",
  65.     "     .*.*.      ",
  66.     "    .  .  .     "));
  67.  
  68.  
  69.     { left and right engine }
  70.     
  71.     SteerLeft :    Array[0..1] of SteerArray=((
  72.     "             .. ",
  73.     "           ..**.",
  74.     "             .. "),(
  75.     "            ... ",
  76.     "          ..*+*.",
  77.     "            ... "));
  78.  
  79.     SteerRight :    Array[0..1] of SteerArray=((
  80.     " ..             ",
  81.     ".**..           ",
  82.     " ..             "),(
  83.     " ...            ",
  84.     ".*+*..          ",
  85.     " ...            "));
  86.  
  87. { variable section }
  88. VAR
  89.     x,y            :    Integer;    { position of the lander }
  90.     xvel,yvel        :    Integer;    { its velocity }
  91.     g            :    Integer;    { graviation constant }
  92.     power            :    Integer;    { acceleration power of the engines }
  93.     powerleft,powerright    :    Integer;
  94.     strength        :    Integer;    { hull strength of the lander. Crash if we hit the ground faster than that }
  95.     w            :    WindowPtr;    { the main window }
  96.     s            :    ScreenPtr;    { the screen }
  97.     lsp            :    SpritePtr;    { the lander sprite }
  98.     botsp,leftsp,rightsp    :    SpritePtr;    { left,right and main engine }
  99.     bot1sp,left1sp,right1sp    :    SpritePtr;    { alternate images, for animation }
  100.     platform        :    Integer;    { position of the landing platform }
  101.     plheight        :    Integer;    { height of the platform }
  102.     fuel,lastfuel        :    Integer;    { current fuel and last fuel, used for easy updateing }
  103.     biguse            :    Integer;    { fuel consumption of the main engine }
  104.     smalluse        :    Integer;    { fuel consumption of the left and right engine }
  105.  
  106. { A tiny note about the positioning of sprites:
  107.   All positions are relative to the window the sprite resides in, regardless
  108.   if this is a true hardware sprite or a bob.
  109.   The position is given as 32 bit scaled fraction - this sounds horrible, but
  110.   is in fact very easy:
  111.     To get the real screen position of the sprite, shift the sprite 
  112.     position right by 16 bits (divide by 65536), i.e. 
  113.         screenx=spritex SHR 16.
  114.  
  115.     To set the sprite to a real screen position, shift the screen position
  116.     left by sixteen bits, i.e. spritex=screenx SHL 16.
  117.  
  118.     Experts might notice that the position is, hence, encoded 
  119.     as following:
  120.  
  121.     spritex    = ssssssssssssssss ffffffffffffffff
  122.           screen position  fractional part
  123.           upper 16 bits       lower 16 bits
  124.  
  125.   This scaling is done to allow smooth animation without floating point
  126.   numbers, which are usually slow. }
  127.         
  128.  
  129. { This procedure draws the fuel gauge at the top of the main window }
  130. Procedure DrawFuel;
  131. BEGIN
  132.     Color(w,2);        { select pen two for drawing }
  133.     PBox(w,0,0,319,8);    { draw the full gauge }
  134.     Position(w,0,7);    { select the position }
  135.     DrawMode(w,0);        { select JAM1 for drawing, i.e. the text background is transparent }
  136.     Color(w,1);        { select pen one for the text }
  137.     DrawText(w,"Fuel :");    { print the text at the selected position }
  138.     Color(w,0);        { select pen 0 for the separator bar }
  139.     Plot(w,99,0);        { draw starting point }
  140.     DrawTo(w,99,8);        { draw the bar, a vertical line }
  141.     lastfuel:=fuel;        { remember the last fuel, for easy update }
  142. END;
  143.  
  144. { This procedure is called when the fuel has changed and must be updated.
  145.   It does not re-draw the fuel gauge completely, only the part that has
  146.   changed is updated. The variable lastfuel holds the last fuel for this
  147.   purpose. }
  148. Procedure UpdateFuel;
  149. BEGIN
  150.     IF fuel<0 THEN    fuel:=0;    { don't draw negative fuel }
  151.  
  152.     IF fuel<>lastfuel THEN BEGIN    { any change whatsoever? }
  153.         Color(w,1);        { select pen 1, black }
  154.             { Update the gauge. Note that the fuel is shifted 
  155.               right by 16 bits, i.e. is divided by 65536.
  156.               This is just a useful scaling to avoid floats }
  157.         PBox(w,100+(fuel SHR 16),0,100+(lastfuel SHR 16),8);
  158.         lastfuel:=fuel        { store the last fuel }
  159.     END
  160. END;
  161.  
  162. { Setup the velocities, position and other lander specific constants.
  163.   If you want to add more levels with different settings, that's definitely
  164.   the procedure you've to modify }    
  165. Procedure InitVelocities;
  166. BEGIN
  167.     x:=160 SHL 16;    { setup x and y position of the lander. Please note }
  168.     y:=8 SHL 16;    { that the screen positions 160,8 must be shifted }
  169.             { left by 16 bits to give the sprite position, as }
  170.             { discussed above }
  171.     xvel:=0;
  172.     yvel:=0;    { set the velocity to zero }
  173.     g:=1024;    { graviational constant. Bigger = harder. }
  174.     power:=4096;        { acceleration by the main engine. }
  175.     powerleft:=2048;    { left and right engine power }
  176.     powerright:=2048;
  177.     strength:=65536;    { hull strength. If we land harder than that,
  178.                   we crash! }
  179.     fuel:=219 SHL 16;    { the fuel }
  180.     biguse:=16384;        { fuel consumption, for left & right and main engine }
  181.     smalluse:=4096;
  182. END;
  183.  
  184. { Draw some stars as background. The color effects are done with the "copper"
  185.   a graphics coprocessor on board. }
  186. Procedure DrawStars;
  187. VAR
  188.     i            :    Integer;
  189. BEGIN
  190.     Color(w,1);        { select color one for the stars }
  191.     FOR i:=0 TO 39 DO    { draw 40 stars at random positions }
  192.         Plot(w,RangeRandom(319),RangeRandom(180)+8)
  193. END;
  194.  
  195. { Draw the landing plot, i.e the ground. }
  196. Procedure DrawGround;
  197. VAR
  198.     i            :    Integer;
  199.     y            :    Integer;
  200.     dice            :    Integer;
  201. BEGIN
  202.     y:=185-RangeRandom(50);            { initial height }
  203.     platform:=RangeRandom(319-18);        { position of the platform }
  204.     Color(w,2);                { select pen two for the ground }
  205.     { the nice color effect is again done by the copper, which alters the
  206.       contents of the color registers "on the fly". No need to take care
  207.       about this right here. }
  208.         
  209.     FOR i:=0 TO 319 DO BEGIN    { for all x positions }
  210.         Plot(w,i,189);        { draw a line from the bottom of }
  211.         DrawTo(w,i,y);        { the window to the height of the ground }
  212.         dice:=RangeRandom(6);    { roll a dice to get the new height }
  213.         IF (i>=platform) AND (i<=platform+17) THEN BEGIN
  214.             dice:=3;    { if we're at the position of the platform }
  215.             plheight:=y    { do not change the height and remember }
  216.                     { the position for later use }
  217.         END;
  218.  
  219.         { depening on the dice roll above, modify the height 
  220.           by -3 to +3 }
  221.  
  222.         CASE dice OF
  223.             0:    IF y<183 THEN y:=y+3;
  224.             1:    IF y<184 THEN y:=y+2;
  225.             2:    IF y<185 THEN y:=y+1;
  226.             4:    IF y>99  THEN y:=y-1;
  227.             5:    IF y>98  THEN y:=y-2;
  228.             6:    IF y>97  THEN y:=y-3;
  229.         END;
  230.     END;
  231.  
  232.     { The ground is now almost complete. We've to draw the platform
  233.         with pen 3 at the position remembered before }
  234.  
  235.     Color(w,3);             { select pen three }
  236.     Plot(w,platform,plheight+3);     { start at the left bottom position }
  237.     DrawTo(w,platform,plheight);     { up to the ground }
  238.     DrawTo(w,platform+17,plheight);  { and now rightwards }
  239.     DrawTo(w,platform+17,plheight+3);{ and down into the ground again }
  240.     Plot(w,platform+1,plheight+3);     { just the same story, but }
  241.     DrawTo(w,platform+1,plheight+1); { with a little displacement }
  242.     DrawTo(w,platform+16,plheight+1);{ for a thicker line }
  243.     DrawTo(w,platform+16,plheight+3);
  244.  
  245. END;
  246.  
  247. { This is the main animation loop, everything important happens in here.
  248.   It returns TRUE on a successful landing, or FALSE on a crash. }
  249. Function AnimationLoop : Boolean;
  250. VAR
  251.     pushleft,pushright    :    Boolean;    { true if joystick is pushed into this direction }
  252.     pushup            :    Boolean;
  253.     boom            :    Boolean;    { true if we crashed }
  254.     landed            :    Boolean;    { true on successful landing }
  255.     c1,c2            :    Byte;        { the colors at the position of the landing gear }
  256.     xw,yw            :    Integer;    { temporary storage }
  257. BEGIN
  258.  
  259.     boom:=FALSE;
  260.     landed:=FALSE;        { neither landed nor crashed }
  261.     ShowSprite(lsp);    { make the lander visible. }
  262.  
  263.     { A tiny remark about all sprite related functions: (Almost) none of
  264.       them has a direct result on the screen. Calls like ShowSprite() or
  265.       PlaceSprite() DO NOT have an immediate effect at all, they just
  266.       alter the current properties of the sprite.
  267.       To make the changes visible, a call to AnimateSprites() or
  268.       RedrawSprites() is necessary. This buffering is done because
  269.       both calls are rather slow, so you should avoid calling them too
  270.       often. Just set all sprite properties before, and map them to
  271.       the screen AT ONCE with one big AnimateSprites().
  272.  
  273.       The only difference between AnimateSprites() and RedrawSprites()
  274.       is that the first one does not only redraw the sprites on the
  275.       screen, but drives the animation engine as well; i.e., it selects
  276.       the next sprite out of an animation sequence. 
  277.       If you don't use any animation, both procedures are equivalent. }
  278.  
  279.     
  280.     REPEAT    { .. until crash or landing }
  281.  
  282.         UpdateFuel;        { redraw the fuel gauge }
  283.         pushup:=StickUp(1);    { true if the user pushed the stick upwards }
  284.         pushleft:=StickLeft(1);    { same for left and right }
  285.         pushright:=StickRight(1);
  286.  
  287.         { the argument to the joystick calls is the port number of
  288.           the joystick, 1 beeing the right one at the A2000, i.e. the
  289.           one which is usually NOT connected to the mouse.
  290.           You MAY use a joystick in port 0 as well, by providing
  291.           a zero as argument, but this will make the mouse unuseable.
  292.           Mouse input can be re-established with a call of
  293.           FreeJoystick(0), if you need it. }
  294.  
  295.         IF pushup AND (fuel>=biguse) THEN BEGIN    { start main engine if fuel is left }
  296.             ShowSprite(botsp);    { show the main engine }
  297.             yvel:=yvel-power;    { accelerate upwards }
  298.             fuel:=fuel-biguse;    { consume fuel }
  299.         END ELSE BEGIN
  300.             HideSprite(botsp);    { hide it }
  301.             yvel:=yvel+g;        { fall down }
  302.         END;
  303.  
  304.         { left and right engines handled similar }
  305.         IF pushleft AND (fuel>=smalluse) THEN BEGIN
  306.             ShowSprite(rightsp);
  307.             xvel:=xvel-powerleft;
  308.             fuel:=fuel-smalluse;
  309.         END ELSE BEGIN
  310.             HideSprite(rightsp);
  311.         END;
  312.  
  313.         IF pushright AND (fuel>=smalluse) THEN BEGIN
  314.             ShowSprite(leftsp);
  315.             xvel:=xvel+powerright;
  316.             fuel:=fuel-smalluse;
  317.         END ELSE BEGIN
  318.             HideSprite(leftsp);
  319.         END;
  320.  
  321.         { The next lines handle boundary collision of the lander.
  322.           The animation engine of the OS can handle sprite to sprite
  323.           collsions, and sprite to boundary collisons, but no
  324.           sprite to playfield collisions. I've no idea why, this is
  325.           in fact a design flaw which can't be fixed with the window-
  326.           lib.
  327.           We use here the sprite to boundary collisions for 
  328.           reflections on the boundary of the window.
  329.     
  330.           The hitmask of the lander was setup in the main program,
  331.           we can read it here with ReadCollisionMask.
  332.           The first argument is the sprite, the last a boolean that
  333.           indicates wether the collision mask should be cleared
  334.           for the next loop. }
  335.  
  336.         { collision with left boundary ? }        
  337.         IF (ReadCollisionMask(lsp,FALSE) AND COLLIDE_LEFT)<>0 THEN BEGIN
  338.             IF xvel<0 THEN
  339.                 xvel:=-xvel    { reflection }
  340.         END;
  341.  
  342.         { collision with right boundary ? }
  343.         IF (ReadCollisionMask(lsp,FALSE) AND COLLIDE_RIGHT)<>0 THEN BEGIN
  344.             IF xvel>0 THEN
  345.                 xvel:=-xvel    { reflection as well }
  346.         END;
  347.  
  348.         { collision with top of window ? }
  349.         IF (ReadCollisionMask(lsp,FALSE) AND COLLIDE_TOP)<>0 THEN BEGIN
  350.             IF yvel<0 THEN
  351.                 yvel:=-yvel;
  352.         END;
  353.  
  354.         { collsion with the bottom is a crash }
  355.         IF (ReadCollisionMask(lsp,TRUE) AND COLLIDE_BOTTOM)<>0 THEN BEGIN
  356.             IF yvel>0 THEN
  357.                 boom:=TRUE
  358.         END;
  359.  
  360.         { update the position by adding the velocity }
  361.         x:=x+xvel;
  362.         y:=y+yvel;
  363.  
  364.         { place the lander at the new position }
  365.         PlaceSprite(lsp,x,y);
  366.         { place the left,right and main engine as well.
  367.           The proper displacement of the sprites was setup
  368.           before, in the main program. We may use here the
  369.           the same position as for the lander.
  370.           Even though the sprite poisitions are set here, this
  371.           does not mean that they are visible. This is controlled
  372.           by ShowSprite & HideSprite }
  373.         PlaceSprite(leftsp,x,y);
  374.         PlaceSprite(rightsp,x,y);
  375.         PlaceSprite(botsp,x,y);
  376.  
  377.  
  378.         { We're now handling collision with the ground. The
  379.           collision routines of the OS do not handle them, sigh.}
  380.  
  381.  
  382.         { Determinate the screen position from the sprite position }
  383.         xw:=x SHR 16;
  384.         yw:=y SHR 16;
  385.  
  386.         IF y>0 THEN BEGIN    { don't allow collision with the fuel gauge }
  387.  
  388.             c1:=Locate(w,xw+1,yw+9); { get the color at the position of the landing gear }
  389.             c2:=Locate(w,xw+14,yw+9);
  390.  
  391.             { don't allow collisions with the stars.
  392.               -1 is returned if the point is outside of the
  393.               window. This may happen if the sprite is about to
  394.               collide with the boundary. We don't handle this as
  395.               a crash }
  396.             IF (c1=1) or (c1=-1) THEN    c1:=0;
  397.             IF (c2=1) or (c2=-1) THEN    c2:=0;
  398.     
  399.             IF ((c1<>0) OR (c2<>0)) THEN BEGIN { any collision? }
  400.                 IF (c1=3) AND (c1=3) THEN BEGIN { with the platform? }
  401.                     IF yvel<strength THEN    { if so, slow enough ? }
  402.                         landed:=TRUE    { yes, so successful landing }
  403.                     ELSE    boom:=TRUE    { if not, crash }
  404.                 END ELSE boom:=TRUE        { same if we collide with anything else }
  405.             END
  406.         END;
  407.  
  408.         { the next procedure redraws all the sprites. Note again
  409.           that (almost) all sprite calls don't work immediately and
  410.           this one IS needed. 
  411.           It does also the animation we've setup for realistic engine }
  412.         AnimateSprites(w);    
  413.  
  414.     UNTIL boom OR landed; {abort on crash or landing}
  415.  
  416.     AnimationLoop:=landed; {return TRUE if landed properly}
  417. END;
  418.  
  419. { This is the main loop of the game. Quite a lot must be done here, as
  420.   for example a score counter, more levels, more "lives", etc... }
  421.     
  422. Procedure MainLoop;
  423. VAR
  424.     success        :    Boolean;
  425. BEGIN
  426.     REPEAT
  427.         HideSprite(lsp);    { hide all sprites }
  428.         HideSprite(botsp);
  429.         HideSprite(leftsp);
  430.         HideSprite(rightsp);
  431.         AnimateSprites(w);    { and make the changes visible }
  432.     
  433.         ClearRaster(w,0);    { clear the window }
  434.         DrawStars;        { draw background }
  435.         DrawGround;        { draw ground }
  436.         InitVelocities;        { initialize lander specific data }
  437.         DrawFuel;        { draw the fuel gauge }
  438.         success:=AnimationLoop;    { and start the animation }
  439.     UNTIL NOT success;        { abort on a crash }
  440. END;
  441.  
  442. { This procedure is used to setup the nice color effects on the screen.
  443.   It uses the "Copper" support procedures of the windowlib to change the
  444.   color of some of the pens in the middle of the screen. }
  445.  
  446. PROCEDURE SetupCopper;
  447. VAR    
  448.     y        :    Integer;
  449.     i        :    Integer;
  450. BEGIN
  451.     SetColor(s,0,0,0,15);    
  452.     { set background to blue. The colors selected with SetColor are
  453.       always visible on top of the screen }
  454.             
  455.  
  456.     y:=19;
  457.     CopperWait(s,0,y);    
  458.     { wait for line 19 for the next copper operation }
  459.  
  460.     CopperSetColor(s,1,14,13,12);
  461.     CopperSetColor(s,2,1,2,3);
  462.     CopperSetColor(s,3,0,15,5);
  463.     { fill pens 1 to 3 with new values }
  464.  
  465.     { dim blue to black }
  466.     FOR i:=0 TO 15 DO BEGIN
  467.         CopperSetColor(s,0,0,0,15-i);    { set pen color }
  468.         y:=y+1;
  469.         CopperWait(s,0,y);        { wait for the next line }
  470.     END;
  471.  
  472.     { dim the stars }
  473.     FOR i:=0 TO 11 DO BEGIN
  474.         CopperSetColor(s,1,12-i,13-i,14-i);    { next color }
  475.         y:=y+8;                { wait for the next }
  476.         CopperWait(s,0,y);        { eight lines }
  477.     END;
  478.  
  479.     { dim the ground from dark to light }
  480.     FOR i:=0 TO 8 DO BEGIN
  481.         CopperSetColor(s,2,2+i,3+i,4+i);    { ground }
  482.         CopperSetColor(s,3,0,15-i,5);        { platform }
  483.         y:=y+7;
  484.         CopperWait(s,0,y);
  485.     END;
  486.  
  487.     { we're done with the copper, display the color effects }
  488.     CopperDone(s);
  489. END;
  490.  
  491.  
  492. { This is now the main program. Initialising starts here }
  493. BEGIN
  494.     { setup the windowlib. This one is VERY important, nothing works
  495.       without it! }
  496.     InitGraphics;    
  497.         
  498.     SelfSeed;    { initialise the random generator }
  499.  
  500.     { open a screen for our program. We use a standard LORES screen with
  501.       four pens. A lot of color effects can be done with the copper, the
  502.       sprites come with their own colors anyways.
  503.       LORES is a MUST if you want to use the hardware sprites with 
  504.       collision detection, due to another bug in the OS routines }
  505.     s:=OpenAScreen(0,0,320,200,2,MON_LORES,"Lander © 1997 THOR-Software.");
  506.         IF s<>NIL THEN BEGIN
  507.         { unless all other calls, this one might fail! You HAVE to
  508.           check if we got the screen, no way around it !}
  509.         
  510.         SetupCopper; { display the graphics effects with the copper }
  511.         
  512.         { open a window on the screen. We use a borderless backdrop window
  513.           here, the frame around the window is not wanted.
  514.           Note that we must pass a NIL pointer as window title to make
  515.           the drag bar of the window invisible as well }
  516.         w:=OpenScreenWindow(s,0,10,320,190,WINFLG_BACKDROP or WINFLG_BORDERLESS or WINFLG_ACTIVATE,NIL);
  517.                 IF w<>NIL THEN BEGIN
  518.             { check if this was successful. THIS CALL MIGHT FAIL! }
  519.         
  520.             IF SetWindowFont(w,"topaz.font",8) THEN BEGIN
  521.                 { set the window font to default. Necessary if
  522.                   the user choose something else.
  523.                   The font will be used by the fuel gauge.
  524.                   Again, this call might fail and returns
  525.                   FALSE in this case }
  526.                 
  527.  
  528.     { the next calls build the sprite shapes of the lander and the
  529.       engines. Each engine uses two sprites, only one of them is
  530.       visible at a time, as done by the animation procedures.
  531.       This gives a nice "fire" effect.
  532.       We use also true hardware sprites as they are faster, less
  533.       flickering and come with their own colors.
  534.       They are, however, limited to LORES }
  535.  
  536.                 lsp:=OpenSprite(w,@Lander[0],10,SPRITE_HARDWARE);
  537.                 botsp:=OpenSprite(w,@Flame[0][0],5,SPRITE_HARDWARE);
  538.                 bot1sp:=OpenSprite(w,@Flame[1][0],5,SPRITE_HARDWARE);
  539.                 leftsp:=OpenSprite(w,@SteerLeft[0][0],3,SPRITE_HARDWARE);
  540.                 left1sp:=OpenSprite(w,@SteerLeft[1][0],3,SPRITE_HARDWARE);
  541.                 rightsp:=OpenSprite(w,@SteerRight[0][0],3,SPRITE_HARDWARE);
  542.                 right1sp:=OpenSprite(w,@SteerRight[1][0],3,SPRITE_HARDWARE);
  543.  
  544.     { please note that we must pass a pointer to the first string of the
  545.       sprite "ASCII Art" definition. The next parameters are the height and a
  546.       flags field }
  547.     
  548.  
  549.     { Link the engine sprites together. This is all required for the
  550.       animation, everything else is done by the windowlib.
  551.       The linked sprites come with their own colors, their own
  552.       displacement set by ShiftSprite, their own shape but share
  553.       the position of the sprite they are linked to. }
  554.  
  555.                 LinkSprite(botsp,bot1sp);
  556.                 LinkSprite(leftsp,left1sp);
  557.                 LinkSprite(rightsp,right1sp);
  558.  
  559.     { Start the collision detection of the lander sprite with the border.
  560.       The first parameter is the "MeMask", used for collisions with other
  561.       sprites, the second is the "HitMask" which selects which collisions
  562.       are detected for this sprite }
  563.     
  564.                 SetCollisionMasks(lsp,0,COLLIDE_BORDER);
  565.     
  566.     { Select the colors for the lander sprite }
  567.                 SetSpriteColor(lsp,1,10,10,10);
  568.                 SetSpriteColor(lsp,2,4,0,8);
  569.                 SetSpriteColor(lsp,3,4,4,4);
  570.  
  571.     { The colors between the engines will be shared. This has 
  572.       two advantages: First, we have to setup the colors only for the
  573.       base sprite, all the colors of all sprites linked to that one will
  574.       get the same colors. Secondly, it tells the sprite mapping that
  575.       a hardware sprite using the same colors can be used to display this
  576.       sprite, allowing more sprites on the display. }      
  577.  
  578.                 ShareColors(botsp,leftsp);
  579.                 ShareColors(botsp,rightsp);
  580.  
  581.                 ShareColors(bot1sp,left1sp);
  582.                 ShareColors(bot1sp,right1sp);
  583.  
  584.     { select the colors for the engines, base and second animation
  585.       frame }
  586.                 SetSpriteColor(botsp,1,15,0,0);
  587.                 SetSpriteColor(botsp,2,15,15,15);
  588.                 SetSpriteColor(botsp,3,15,10,0);
  589.  
  590.                 SetSpriteColor(bot1sp,1,10,0,0);
  591.                 SetSpriteColor(bot1sp,2,10,10,15);
  592.                 SetSpriteColor(bot1sp,3,15,8,0);
  593.  
  594.     { select displacements for the engines }
  595.                 ShiftSprite(leftsp,-(14 SHL 16),3 SHL 16);
  596.                 ShiftSprite(rightsp,14 SHL 16,3 SHL 16);
  597.                 ShiftSprite(botsp,0,9 SHL 16);
  598.                 ShiftSprite(left1sp,-(14 SHL 16),3 SHL 16);
  599.                 ShiftSprite(right1sp,14 SHL 16,3 SHL 16);
  600.                 ShiftSprite(bot1sp,0,9 SHL 16);
  601.  
  602.     { start the game }
  603.                 MainLoop;
  604.     
  605.     { close the sprites. Just a matter of good style! }
  606.  
  607.                 CloseSprite(lsp);
  608.                 CloseSprite(botsp);
  609.                 CloseSprite(bot1sp);
  610.                 CloseSprite(leftsp);
  611.                 CloseSprite(left1sp);
  612.                 CloseSprite(rightsp);
  613.                 CloseSprite(right1sp)
  614.             END;    
  615.     { close the window we've used }
  616.             CloseAWindow(w)
  617.         END;
  618.  
  619.     { same for the screen }
  620.         CloseAScreen(s)
  621.     END;
  622.  
  623.     { THIS ONE IS VERY IMPORTANT! It gives all used resources back to
  624.       the system. This is necessary, even though we've closed the
  625.       window, screen and sprites manually. The windowlib allocates
  626.       quite more stuff implicitly than is visible from the outside. }
  627.     ExitGraphics
  628. END.
  629.